using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using Game500.Client.Src.Logic;
using Game500.Client.Src.Networking;
using Game500.ClientServerCommon.Src;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

//using log4net;

namespace Game500.Client.Src
{
  /// <summary>
  /// This is the main type for your game
  /// </summary>
  public class MainGame : Game
  {
#if DEBUG
    [DllImport("kernel32.dll", EntryPoint = "AllocConsole", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    private static extern int AllocConsole();
#endif

    //private static readonly ILog Log = LogManager.GetLogger(typeof(MainGame));

    private readonly GraphicsDeviceManager myGraphics;
    private readonly BaseMapManager myMapManager;
    private readonly string myNetworkMode = "tcp";
    private readonly string myRemoteAddress = "127.0.0.1";
    private readonly int myRemotePort = 5050;
    private readonly string myUsername;
    private ClientNetworkManager myClientNetworkManager;
    private ResourceManager myResourceManager;

    private GameState myGameState = GameState.NotStarted;
    private ObjectManager myObjectManager;
    private bool myFlipFlag = true;
    private FxManager myFxManager;
    private bool myShootFlag = true;

    public MainGame(IPEndPoint serverAddress, string username, BaseMapManager mapManager)
    {
#if DEBUG
      AllocConsole();
#endif
      myUsername = username;
      myMapManager = mapManager;
      myRemoteAddress = serverAddress.Address.ToString();
      myRemotePort = serverAddress.Port;
      myGraphics = new GraphicsDeviceManager(this);
    }

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related resource.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {
      //Log.Info("Starting game...");
      myGraphics.PreferredBackBufferWidth = 1024;
      myGraphics.PreferredBackBufferHeight = 600;
      myGraphics.ApplyChanges();
      base.Initialize();

      myClientNetworkManager = ClientNetworkManager.Create(myNetworkMode);
      Content.RootDirectory = "Content";
      myResourceManager = new ResourceManager(Content, myGraphics.GraphicsDevice, "..\\Media");

      myFxManager = new FxManager(Content);

      myObjectManager = new ObjectManager(myFxManager, myGraphics, myResourceManager);
      myClientNetworkManager.Initialize();
      myClientNetworkManager.GameStartedEvent += OnGameStarted;
      myClientNetworkManager.Connect(myRemoteAddress, myRemotePort);
      myClientNetworkManager.SendGamerInfo(new GamerInfo {Username = myUsername});

      myClientNetworkManager.ShotsEvent += OnShotEventHandler;
      myClientNetworkManager.ExplosionsEvent += OnExplodeHandler;
    }

    private void OnExplodeHandler(ExplosionsEvent ev)
    {
      foreach (var obj in myObjectManager.Objects.Where(obj => ev.ObjectIds.Contains(obj.Key)))
      {
        myFxManager.CreateExplosion(obj.Value);
      }
    }

    private void OnShotEventHandler(ShotsEvent ev)
    {
      if (ev.ShooterIds.Count != 0) myFxManager.Fire();
    }

    private void OnGameStarted()
    {
      myGameState = GameState.Playing;
      GameInfo info = myClientNetworkManager.GameInfo;

      myObjectManager.StartGame(info, myMapManager.GetMapNotNull(info.MapId));
    }

    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your resource.
    /// </summary>
    protected override void LoadContent()
    {
      // Create a new SpriteBatch, which can be used to draw textures.

      // TODO: use this.Content to load your game resource here
    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all resource.
    /// </summary>
    protected override void UnloadContent()
    {
      // TODO: Unload any non ResourceManager resource here
    }

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime)
    {
      KeyboardState state = Keyboard.GetState(PlayerIndex.One);
      if (state.IsKeyDown(Keys.Escape)) Exit();

      if (myGameState != GameState.Playing) return;

      if (state.IsKeyDown(Keys.Left) || state.IsKeyDown(Keys.A))
        myClientNetworkManager.AddAction(new GamerAction(GamerActionType.TangageChangeAction, -1));

      else if (state.IsKeyDown(Keys.Right) || state.IsKeyDown(Keys.D))
        myClientNetworkManager.AddAction(new GamerAction(GamerActionType.TangageChangeAction, 1));

      if (state.IsKeyDown(Keys.Up) || state.IsKeyDown(Keys.W))
      {
        myClientNetworkManager.AddAction(new GamerAction(GamerActionType.ThrottleChangeAction, 1));
        myFxManager.Boost = true;
      }
      else myFxManager.Boost = false;

      if (state.IsKeyDown(Keys.Space) && myShootFlag)
      {
         myClientNetworkManager.AddAction(new GamerAction { Type = GamerActionType.FireAction });
        myShootFlag = false;
      }
      if (state.IsKeyUp(Keys.Space)) myShootFlag = true;


      if (state.IsKeyDown(Keys.F) && myFlipFlag)
      {
        myClientNetworkManager.AddAction(new GamerAction {Type = GamerActionType.FlipAction});
        myFlipFlag = false;
      }

      if (state.IsKeyUp(Keys.F)) myFlipFlag = true;

      myClientNetworkManager.Update();

      SceneFrame frame = myClientNetworkManager.GetActualFrame();
      if (frame != null)
      {
        myObjectManager.Update(frame, gameTime);
      }

      base.Update(gameTime);
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {
      GraphicsDevice.Clear(Color.White);

      myObjectManager.UpdateAll((float)(gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f));

      myObjectManager.RenderAll();

      base.Draw(gameTime);
    }

    #region Nested type: GameState

    private enum GameState
    {
      NotStarted,
      //WaitRoom,
      //WaitForStart,
      Playing
    }

    #endregion
  }
}